
if (!("console" in window) || !("firebug" in console)) {
    (function(){
        window.console = {
            log: function(){
                logFormatted(arguments, "");
            },
            
            debug: function(){
                logFormatted(arguments, "debug");
            },
            
            info: function(){
                logFormatted(arguments, "info");
            },
            
            warn: function(){
                logFormatted(arguments, "warning");
            },
            
            error: function(){
                logFormatted(arguments, "error");
            },
            
            assert: function(truth, message){
                if (!truth) {
                    var args = [];
                    for (var i = 1; i < arguments.length; ++i) {
                        args.push(arguments[i]);
                    }
                    logFormatted(args.length ? args : ["Assertion Failure"], "error");
                    throw message ? message : "Assertion Failure";
                }
            },
            
            dir: function(object){
                var html = [];
                
                var pairs = [];
                for (var name in object) {
                    try {
                        pairs.push([name, object[name]]);
                    } 
                    catch (exc) {
                    }
                }
                
                pairs.sort(function(a, b){
                    return a[0] < b[0] ? -1 : 1;
                });
                
                html.push('<table>');
                for (var i = 0; i < pairs.length; ++i) {
                    var name = pairs[i][0], value = pairs[i][1];
                    
                    html.push('<tr>', '<td class="propertyNameCell"><span class="propertyName">', escapeHTML(name), '</span></td>', '<td><span class="propertyValue">');
                    appendObject(value, html);
                    html.push('</span></td></tr>');
                }
                html.push('</table>');
                
                logRow(html, "dir");
            },
            
            dirxml: function(node){
                var html = [];
                
                appendNode(node, html);
                logRow(html, "dirxml");
            },
            
            group: function(){
                logRow(arguments, "group", pushGroup);
            },
            
            groupEnd: function(){
                logRow(arguments, "", popGroup);
            },
            
            time: function(name){
                timeMap[name] = (new Date()).getTime();
            },
            
            timeEnd: function(name){
                if (name in timeMap) {
                    var delta = (new Date()).getTime() - timeMap[name];
                    logFormatted([name + ":", delta + "ms"]);
                    delete timeMap[name];
                }
            },
            
            count: function(){
                this.warn(["count() not supported."]);
            },
            
            trace: function(){
                this.warn(["trace() not supported."]);
            },
            
            profile: function(){
                this.warn(["profile() not supported."]);
            },
            
            profileEnd: function(){
            },
            
            clear: function(){
                consoleBody.innerHTML = "";
            },
            
            open: function(){
                toggleConsole(true);
            },
            
            close: function(){
                if (frameVisible) 
                    toggleConsole();
            }
        };
        
        // ********************************************************************************************
        
        var consoleFrame = null;
        var consoleBody = null;
        var commandLine = null;
        
        var frameVisible = false;
        var messageQueue = [];
        var groupStack = [];
        var timeMap = {};
        
        var clPrefix = ">>> ";
        
        var isFirefox = navigator.userAgent.indexOf("Firefox") != -1;
        var isIE = navigator.userAgent.indexOf("MSIE") != -1;
        var isOpera = navigator.userAgent.indexOf("Opera") != -1;
        var isSafari = navigator.userAgent.indexOf("AppleWebKit") != -1;
        
        // ********************************************************************************************
        
        function toggleConsole(forceOpen){
            frameVisible = forceOpen || !frameVisible;
            if (consoleFrame) {
                consoleFrame.style.visibility = frameVisible ? "visible" : "hidden";
            }
            else {
                waitForBody();
            }
        };
        
        function focusCommandLine(){
            toggleConsole(true);
            if (commandLine) {
                commandLine.focus();
            }
        };
        
        function waitForBody(){
            if (document.body) {
                createFrame();
            }
            else {
                setTimeout(waitForBody, 200);
            }
        };
        
        function createFrame(){
            if (consoleFrame) 
                return;
            
            window.onFirebugReady = function(doc){
                window.onFirebugReady = null;
                
                var toolbar = doc.getElementById("toolbar");
                toolbar.onmousedown = onSplitterMouseDown;
                
                commandLine = doc.getElementById("commandLine");
                addEvent(commandLine, "keydown", onCommandLineKeyDown);
                
                addEvent(doc, isIE || isSafari ? "keydown" : "keypress", onKeyDown);
                
                consoleBody = doc.getElementById("log");
                layout();
                flush();
            };
            
            var baseURL = getFirebugURL();
            
            consoleFrame = document.createElement("iframe");
            consoleFrame.setAttribute("src", baseURL + "/firebug.html");
            consoleFrame.setAttribute("frameBorder", "0");
            consoleFrame.style.visibility = (frameVisible ? "visible" : "hidden");
            consoleFrame.style.zIndex = "2147483647";
            consoleFrame.style.position = "fixed";
            consoleFrame.style.width = "100%";
            consoleFrame.style.left = "0";
            consoleFrame.style.bottom = "0";
            consoleFrame.style.height = "200px";
            document.body.appendChild(consoleFrame);
        };
        
        function getFirebugURL(){
            var scripts = document.getElementsByTagName("script");
            for (var i = 0; i < scripts.length; ++i) {
                if (scripts[i].src.indexOf("firebug.js") != -1) {
                    var lastSlash = scripts[i].src.lastIndexOf("/");
                    return scripts[i].src.substr(0, lastSlash);
                }
            }
        };
        
        function evalCommandLine(){
            var text = commandLine.value;
            commandLine.value = "";
            
            logRow([clPrefix, text], "command");
            
            var value;
            try {
                value = eval(text);
            } 
            catch (exc) {
            }
            
            console.log(value);
        };
        
        function layout(){
            var toolbar = consoleBody.ownerDocument.getElementById("toolbar");
            var height = consoleFrame.offsetHeight - (toolbar.offsetHeight + commandLine.offsetHeight);
            consoleBody.style.top = toolbar.offsetHeight + "px";
            consoleBody.style.height = height + "px";
            
            commandLine.style.top = (consoleFrame.offsetHeight - commandLine.offsetHeight) + "px";
        };
        
        function logRow(message, className, handler){
            if (consoleBody) {
                writeMessage(message, className, handler);
            }
            else {
                messageQueue.push([message, className, handler]);
                waitForBody();
            }
        };
        
        function flush(){
            var queue = messageQueue;
            messageQueue = [];
            
            for (var i = 0; i < queue.length; ++i) {
                writeMessage(queue[i][0], queue[i][1], queue[i][2]);
            }
        };
        
        function writeMessage(message, className, handler){
            var isScrolledToBottom = consoleBody.scrollTop + consoleBody.offsetHeight >= consoleBody.scrollHeight;
            
            if (!handler) {
                handler = writeRow;
            }
            handler(message, className);
            
            if (isScrolledToBottom) {
                consoleBody.scrollTop = consoleBody.scrollHeight - consoleBody.offsetHeight;
            }
        };
        
        function appendRow(row){
            var container = groupStack.length ? groupStack[groupStack.length - 1] : consoleBody;
            container.appendChild(row);
        };
        
        function writeRow(message, className){
            var row = consoleBody.ownerDocument.createElement("div");
            row.className = "logRow" + (className ? " logRow-" + className : "");
            row.innerHTML = message.join("");
            appendRow(row);
        };
        
        function pushGroup(message, className){
            logFormatted(message, className);
            
            var groupRow = consoleBody.ownerDocument.createElement("div");
            groupRow.className = "logGroup";
            var groupRowBox = consoleBody.ownerDocument.createElement("div");
            groupRowBox.className = "logGroupBox";
            groupRow.appendChild(groupRowBox);
            appendRow(groupRowBox);
            groupStack.push(groupRowBox);
        };
        
        function popGroup(){
            groupStack.pop();
        };
        
        // ********************************************************************************************
        
        function logFormatted(objects, className){
            var html = [];
            
            var format = objects[0];
            var objIndex = 0;
            
            if (typeof(format) != "string") {
                format = "";
                objIndex = -1;
            }
            
            var parts = parseFormat(format);
            for (var i = 0; i < parts.length; ++i) {
                var part = parts[i];
                if (part && typeof(part) == "object") {
                    var object = objects[++objIndex];
                    part.appender(object, html);
                }
                else {
                    appendText(part, html);
                }
            }
            
            for (var i = objIndex + 1; i < objects.length; ++i) {
                appendText(" ", html);
                
                var object = objects[i];
                if (typeof(object) == "string") {
                    appendText(object, html);
                }
                else {
                    appendObject(object, html);
                }
            }
            
            logRow(html, className);
        };
        
        function parseFormat(format){
            var parts = [];
            
            var reg = /((^%|[^\\]%)(\d+)?(\.)([a-zA-Z]))|((^%|[^\\]%)([a-zA-Z]))/;
            var appenderMap = {
                s: appendText,
                d: appendInteger,
                i: appendInteger,
                f: appendFloat
            };
            
            for (var m = reg.exec(format); m; m = reg.exec(format)) {
                var type = m[8] ? m[8] : m[5];
                var appender = type in appenderMap ? appenderMap[type] : appendObject;
                var precision = m[3] ? parseInt(m[3]) : (m[4] == "." ? -1 : 0);
                
                parts.push(format.substr(0, m[0][0] == "%" ? m.index : m.index + 1));
                parts.push({
                    appender: appender,
                    precision: precision
                });
                
                format = format.substr(m.index + m[0].length);
            }
            
            parts.push(format);
            
            return parts;
        };
        
        function escapeHTML(value){
            function replaceChars(ch){
                switch (ch) {
                    case "<":
                        return "&lt;";
                    case ">":
                        return "&gt;";
                    case "&":
                        return "&amp;";
                    case "'":
                        return "&#39;";
                    case '"':
                        return "&quot;";
                }
                return "?";
            };
            return String(value).replace(/[<>&"']/g, replaceChars);
        };
        
        function objectToString(object){
            try {
                return object + "";
            } 
            catch (exc) {
                return null;
            }
        };
        
        // ********************************************************************************************
        
        function appendText(object, html){
            html.push(escapeHTML(objectToString(object)));
        };
        
        function appendNull(object, html){
            html.push('<span class="objectBox-null">', escapeHTML(objectToString(object)), '</span>');
        };
        
        function appendString(object, html){
            html.push('<span class="objectBox-string">&quot;', escapeHTML(objectToString(object)), '&quot;</span>');
        };
        
        function appendInteger(object, html){
            html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>');
        };
        
        function appendFloat(object, html){
            html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>');
        };
        
        function appendFunction(object, html){
            var reName = /function ?(.*?)\(/;
            var m = reName.exec(objectToString(object));
            var name = m ? m[1] : "function";
            html.push('<span class="objectBox-function">', escapeHTML(name), '()</span>');
        };
        
        function appendObject(object, html){
            try {
                if (object == undefined) {
                    appendNull("undefined", html);
                }else if (object == null) {
                    appendNull("null", html);
                }else if (typeof object == "string") {
                    appendString(object, html);
                }else if (typeof object == "number") {
                    appendInteger(object, html);
                }else if (typeof object == "function") {
                    appendFunction(object, html);
                }else if (object.nodeType == 1) {
                    appendSelector(object, html);
                }else if (typeof object == "object") {
                    appendObjectFormatted(object, html);
                }else {
                    appendText(object, html);
                }
            }catch (exc) {
				
            }
        };
        
        function appendObjectFormatted(object, html){
            var text = objectToString(object);
            var reObject = /\[object (.*?)\]/;
            
            var m = reObject.exec(text);
            html.push('<span class="objectBox-object">', m ? m[1] : text, '</span>')
        };
        
        function appendSelector(object, html){
            html.push('<span class="objectBox-selector">');
            
            html.push('<span class="selectorTag">', escapeHTML(object.nodeName.toLowerCase()), '</span>');
            if (object.id) {
				html.push('<span class="selectorId">#', escapeHTML(object.id), '</span>');
			}	
            if (object.className) {
				html.push('<span class="selectorClass">.', escapeHTML(object.className), '</span>');
			}	
            html.push('</span>');
        };
        
        function appendNode(node, html){
            if (node.nodeType == 1) {
                html.push('<div class="objectBox-element">', '&lt;<span class="nodeTag">', node.nodeName.toLowerCase(), '</span>');
                
                for (var i = 0; i < node.attributes.length; ++i) {
                    var attr = node.attributes[i];
                    if (!attr.specified) 
                        continue;
                    
                    html.push('&nbsp;<span class="nodeName">', attr.nodeName.toLowerCase(), '</span>=&quot;<span class="nodeValue">', escapeHTML(attr.nodeValue), '</span>&quot;')
                }
                
                if (node.firstChild) {
					html.push('&gt;</div><div class="nodeChildren">');
					
					for (var child = node.firstChild; child; child = child.nextSibling) 
						appendNode(child, html);
					
					html.push('</div><div class="objectBox-element">&lt;/<span class="nodeTag">', node.nodeName.toLowerCase(), '&gt;</span></div>');
				}else {
					html.push('/&gt;</div>');
				}	
            }else if (node.nodeType == 3) {
                html.push('<div class="nodeText">', escapeHTML(node.nodeValue), '</div>');
            }
        };
        
        // ********************************************************************************************
        
        function addEvent(object, name, handler){
            if (document.all) {
				object.attachEvent("on" + name, handler);
			}else {
				object.addEventListener(name, handler, false);
			}	
        };
        
        function removeEvent(object, name, handler){
            if (document.all) {
				object.detachEvent("on" + name, handler);
			}
			else {
				object.removeEventListener(name, handler, false);
			}	
        };
        
        function cancelEvent(event){
            if (document.all) {
				event.cancelBubble = true;
			}
			else {
				event.stopPropagation();
			}
        };
        
        function onError(msg, href, lineNo){
            var html = [];
            
            var lastSlash = href.lastIndexOf("/");
            var fileName = lastSlash == -1 ? href : href.substr(lastSlash + 1);
            
            html.push('<span class="errorMessage">', msg, '</span>', '<div class="objectBox-sourceLink">', fileName, ' (line ', lineNo, ')</div>');
            
            logRow(html, "error");
        };
        
        function onKeyDown(event){
            if (event.keyCode == 123) 
                toggleConsole();
            else 
                if ((event.keyCode == 108 || event.keyCode == 76) && event.shiftKey &&
                (event.metaKey || event.ctrlKey)) 
                    focusCommandLine();
                else 
                    return;
            
            cancelEvent(event);
        };
        
        function onSplitterMouseDown(event){
            if (isSafari || isOpera) {
				return;
			}
            addEvent(document, "mousemove", onSplitterMouseMove);
            addEvent(document, "mouseup", onSplitterMouseUp);
            
            for (var i = 0; i < frames.length; ++i) {
                addEvent(frames[i].document, "mousemove", onSplitterMouseMove);
                addEvent(frames[i].document, "mouseup", onSplitterMouseUp);
            }
        };
        
        function onSplitterMouseMove(event){
            var win = document.all ? event.srcElement.ownerDocument.parentWindow : event.target.ownerDocument.defaultView;
            
            var clientY = event.clientY;
            if (win != win.parent) {
				clientY += win.frameElement ? win.frameElement.offsetTop : 0;
			}
            var height = consoleFrame.offsetTop + consoleFrame.clientHeight;
            var y = height - clientY;
            
            consoleFrame.style.height = y + "px";
            layout();
        };
        
        function onSplitterMouseUp(event){
            removeEvent(document, "mousemove", onSplitterMouseMove);
            removeEvent(document, "mouseup", onSplitterMouseUp);
            
            for (var i = 0; i < frames.length; ++i) {
                removeEvent(frames[i].document, "mousemove", onSplitterMouseMove);
                removeEvent(frames[i].document, "mouseup", onSplitterMouseUp);
            }
        };
        
        function onCommandLineKeyDown(event){
            if (event.keyCode == 13) {
				evalCommandLine();
			}else if (event.keyCode == 27) {
				commandLine.value = "";
			}		
        };
        
        // window.onerror = onError;
        addEvent(document, isIE || isSafari ? "keydown" : "keypress", onKeyDown);
        
        if (document.documentElement.getAttribute("debug") == "true") {
			toggleConsole(true);
		}	
    })();
}
